﻿using Smile.Imaging.Exif;
using Smile.Mvvm.Model;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;

namespace Smile.Imaging
{
    public class ImageJpeg : ModelBase, IImage
    {
        // @Properties
        public int Width
        {
            get 
            {
                return _jpegInfo.Width;
            }
        }

        public int Heigth
        {
            get 
            {
                return _jpegInfo.Height;
            }
        }

        public int Size
        {
            get 
            {
                return _jpegInfo.FileSize;
            }
        }

        public string Extension
        {
            get
            {
                return _extension;
            }
        }

        public string Filename
        {
            get
            {
                return _jpegInfo.FileName;
            }
            set
            {
                _jpegInfo.FileName = value;
            }
        }

        public Stream ImageStream
        {
            get 
            {
                return _imageStream;
            }
        }


        // @Public
        public ImageJpeg(Stream imageStream, string filename)
        {
            imageStream.Position = 0;
            imageStream.CopyTo(_imageStream);
            _jpegInfo = ExifReader.ReadJpeg(_imageStream, filename);
            Rotate(_jpegInfo.Orientation);
        }

        public IImage Resize(int width, int heigth, ImageResizeMode mode = ImageResizeMode.KeepRatio)
        {
            int newWidth = Width;
            int newHeigth = Heigth;

            if (mode == ImageResizeMode.KeepRatio)
            {
                // Horizontal adjustiong
                if (newWidth > width)
                {
                    int ratio = newWidth * 100 / width;
                    newWidth = width;
                    newHeigth = newHeigth * 100 / ratio;
                }

                // Vertical adjusting
                if (newHeigth > heigth)
                {
                    int ration = newHeigth * 100 / heigth;
                    newHeigth = heigth;
                    newWidth = newWidth * 100 / ration;
                }
            }

            // Saving image
            BitmapImage bitmapImage = new BitmapImage();
            _imageStream.Position = 0;
            bitmapImage.SetSource(_imageStream);
            WriteableBitmap wBitmap = new WriteableBitmap(bitmapImage);
            MemoryStream newImageStream = new MemoryStream();
            wBitmap.SaveJpeg(newImageStream, newWidth, newHeigth, 0, 100);
            return new ImageJpeg(newImageStream, Filename);
        }

        public async Task<string> ToBase64Async(ImageBase64Mode mode = ImageBase64Mode.Common)
        {
            return await Task<string>.Run(() => ToBase64(mode));
        }


        // @Protected
        protected JpegInfo          _jpegInfo;
        protected readonly string   _extension = "jpeg";
        protected MemoryStream      _imageStream = new MemoryStream();


        protected void Rotate(ExifOrientation orientation)
        {
            int angle = 0;

            switch (orientation)
            {
                case ExifOrientation.TopRight:
                    angle = 90;
                    break;
                case ExifOrientation.BottomRight:
                    angle = 180;
                    break;
                case ExifOrientation.BottomLeft:
                    angle = 270;
                    break;
                default:
                    break;
            }

            Rotate(angle);
        }

        protected void Rotate(int angle)
        {
            // Verify angle
            if (angle % 90 != 0 || angle < 0)
            {
                throw new ArgumentException("ImageJpeg: Invalid angle");
            }

            BitmapImage bitmapImage = new BitmapImage();
            _imageStream.Position = 0;
            bitmapImage.SetSource(_imageStream);
            WriteableBitmap wbSource = new WriteableBitmap(bitmapImage);

            // Define image size
            WriteableBitmap wbTarget = null;
            if (angle == 0)
            {
                wbTarget = wbSource;
            }
            else if (angle % 180 == 0)
            {
                wbTarget = new WriteableBitmap(wbSource.PixelWidth, wbSource.PixelHeight);
            }
            else
            {
                wbTarget = new WriteableBitmap(wbSource.PixelHeight, wbSource.PixelWidth);
            }

            // Copying image
            for (int x = 0; x < wbSource.PixelWidth; ++x)
            {
                for (int y = 0; y < wbSource.PixelHeight; ++y)
                {
                    switch (angle % 360)
                    {
                        case 90:
                            wbTarget.Pixels[(wbSource.PixelHeight - y - 1) + x * wbTarget.PixelWidth] = wbSource.Pixels[x + y * wbSource.PixelWidth];
                            break;
                        case 180:
                            wbTarget.Pixels[(wbSource.PixelWidth - x - 1) + (wbSource.PixelHeight - y - 1) * wbSource.PixelWidth] = wbSource.Pixels[x + y * wbSource.PixelWidth];
                            break;
                        case 270:
                            wbTarget.Pixels[y + (wbSource.PixelWidth - x - 1) * wbTarget.PixelWidth] = wbSource.Pixels[x + y * wbSource.PixelWidth];
                            break;
                    }
                }
            }

            // Saving changes
            MemoryStream newImageStream = new MemoryStream();
            wbTarget.SaveJpeg(newImageStream, wbTarget.PixelWidth, wbTarget.PixelHeight, 0, 100);
            _imageStream = newImageStream;
            UpdateInformations();
        }

        protected void UpdateInformations()
        {
            _imageStream.Position = 0;
            _jpegInfo = ExifReader.ReadJpeg(_imageStream, Filename);
            RaisePropertyChanged("Width");
            RaisePropertyChanged("Heigth");
            RaisePropertyChanged("Size");
            RaisePropertyChanged("Filename");
            RaisePropertyChanged("ImageStream");
        }

        protected string ToBase64(ImageBase64Mode mode = ImageBase64Mode.Common)
        {
            string base64String = Convert.ToBase64String(_imageStream.ToArray());

            if (mode == ImageBase64Mode.Common)
            {
                base64String = base64String.Replace(" ", "+");
            }

            return base64String;
        }
    }
}
